function one() {
  var a = 1;
  var name = 'one';
  function two() {
    var b = 2;
    var name = 'two';
    function three() {
      var c = 3;
      var name = 'three';
      return () => {
        var d = 4;
        debugger
        console.log(a, b, c, d);
      }
    }
    return three();
  }
  return two();
}

/** 为什么说闭包会导致内存泄漏?
 * 像本例中,【全局】声明了一个fn,【且】这个fn是一个闭包函数,故closure永远不会销毁,除非关闭网页(或则手动将fn=null,但这不大可能,因为已经上线了,改不了代码). */
var fn = one();

fn()/*fnEC*/;

let globalEC = {
  // this: globalThis,
  this: this,
  outer: null,
  variableEnvironment: { one() { } },
  lexicalEnvironment:[]
};
/** ↓下面省略了 执行编译 直接显示执行阶段最后执行上下文还没有销毁时的结果*/
let oneEC = {
  this: this,
  outer: globalEC,
  variableEnvironment: { a: 1, two() { }, name: 'one' },
  lexicalEnvironment:[]
};
let twoEC = {
  this: this,
  outer: oneEC,
  variableEnvironment: { b: 2, three() { }, name: 'two' },
  lexicalEnvironment:[]
};
let threeEC = {
  this: this,
  outer: twoEC,
  variableEnvironment: { c: 3, name: 'three' },
  lexicalEnvironment:[]
};

let fnEC = {
  this: this,
  outer: globalEC, /** var fn = one();*/

  //Closure(three) Closure(two) Closure(one)
  //只会留下用到的属性,其它的会被回收掉
  closures: [{ a: 1 }, { b: 2 }, { c: 3 }], /** ←closures放执行上下文里这样写不大好，因为closures并不会随着当前执行上下文销毁而销毁.
   这里的closures是那些上级作用域里的词法和变量环境中剩下的没有被销毁的当前函数运行会用到的变量的引用,并且当前fnEC执行上下文在函数执行完后销毁时 也不会被销毁*/

  variableEnvironment:{d:4},
  lexicalEnvironment:[]
};
console.log(getValue('a', fnEC), getValue('b', fnEC), getValue('c', fnEC), getValue('d', fnEC));

function getValue(name, ec) {
  /** 1. 先从自己的词法栈里找*/
  if (ec.lexicalEnvironment.length > 0) {
    for (let i = ec.lexicalEnvironment.length - 1; i >= 0; i--) {
      if (name in ec.lexicalEnvironment[i]) {
        return ec.lexicalEnvironment[i][name];
      }
    }
  }

  /** 2. 再从变量环境中找*/
  if(name in ec.variableEnvironment){
    return ec.variableEnvironment[name];
  }

  /** 3. 再找闭包*/
  for(let i = ec.closures.length -1; i>=0; i--){
    if(name in ec.closures[i]){
      return ec.closures[i][name];
    }
  }

  /** 4. 再从上级作用域里按上面的方式依次查找 如此循环*/
  if(ec.outer) {
    return getValue(name, ec.outer);
  }

  return null;
}

